package tracex

import (
	"context"
	"time"

	"github.com/todo/toolx/utilx/otlex"
	"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
	"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
	"go.opentelemetry.io/otel/sdk/trace"
	"google.golang.org/grpc/credentials"
)

type Config otlex.ExporterConfig

func (config Config) NewClient(handles ...func(config *Config)) otlptrace.Client {
	for _, handle := range handles {
		handle(&config)
	}

	switch config.Type {
	case otlex.Grpc:
		return otlptracegrpc.NewClient(config.OtlpTraceGrpcOption()...)
	case otlex.Http:
		return otlptracehttp.NewClient(config.OtlpTraceHttpOption()...)

	default:
		panic(otlex.NotFoundType)
	}
}

func (config Config) OtlpTraceGrpcOption() []otlptracegrpc.Option {
	options := make([]otlptracegrpc.Option, 0, 1<<3)
	options = append(options,
		otlptracegrpc.WithEndpoint(config.Endpoint),
		otlptracegrpc.WithHeaders(config.Headers),
		otlptracegrpc.WithTimeout(time.Second*time.Duration(config.Timeout)),
		otlptracegrpc.WithRetry(otlptracegrpc.RetryConfig{
			Enabled:         config.Retry.Enabled,
			InitialInterval: config.Retry.InitialInterval,
			MaxInterval:     config.Retry.MaxInterval,
			MaxElapsedTime:  config.Retry.MaxElapsedTime,
		}),
		otlptracegrpc.WithReconnectionPeriod(time.Second*time.Duration(config.ReconnectionPeriod)),
		otlptracegrpc.WithServiceConfig(config.ServiceConfig),
	)

	if config.Compression {
		options = append(options, otlptracegrpc.WithCompressor("gzip"))
	}

	if !config.Insecure && config.TLS.IsEnable() {
		options = append(options, otlptracegrpc.WithTLSCredentials(credentials.NewTLS(config.TLS.Tls())))
	}

	if config.Insecure {
		options = append(options, otlptracegrpc.WithInsecure())
	}

	return options
}

func (config Config) OtlpTraceHttpOption() []otlptracehttp.Option {
	options := make([]otlptracehttp.Option, 0, 1<<3)
	options = append(options, otlptracehttp.WithEndpointURL(config.Endpoint),
		otlptracehttp.WithHeaders(config.Headers),
		otlptracehttp.WithTimeout(time.Second*time.Duration(config.Timeout)),
		otlptracehttp.WithRetry(otlptracehttp.RetryConfig{
			Enabled:         config.Retry.Enabled,
			InitialInterval: config.Retry.InitialInterval,
			MaxInterval:     config.Retry.MaxInterval,
			MaxElapsedTime:  config.Retry.MaxElapsedTime,
		}),
	)

	if config.Compression {
		options = append(options, otlptracehttp.WithCompression(1))
	}

	if !config.Insecure && config.TLS.IsEnable() {
		options = append(options, otlptracehttp.WithTLSClientConfig(config.TLS.Tls()))
	}

	if config.Insecure {
		options = append(options, otlptracehttp.WithInsecure())
	}

	if len(config.URLPath) > 0 {
		options = append(options, otlptracehttp.WithURLPath(config.URLPath))
	}

	return options
}

func NewSpanExporter(ctx context.Context, config Config) (trace.SpanExporter, error) {
	switch config.Type {
	case otlex.Grpc, otlex.Http:
		return otlptrace.New(ctx, config.NewClient())
	case otlex.Stdout:
		return stdouttrace.New(
			stdouttrace.WithoutTimestamps(),
			stdouttrace.WithPrettyPrint(),
		)
	}
	return nil, otlex.NotFoundType
}
